#include "CustomWindow.h"


CustomWindow::CustomWindow()	
{
	frameMargins.cxLeftWidth = 0;
	frameMargins.cxRightWidth = 0;
	frameMargins.cyBottomHeight = 0;
	frameMargins.cyTopHeight = 0;

	glassMargins.cxLeftWidth = -1;
	glassMargins.cxRightWidth = -1;
	glassMargins.cyBottomHeight = -1;
	glassMargins.cyTopHeight = -1;

	wndProcPtr = 0;
}

CustomWindow::~CustomWindow()
{
}


void CustomWindow::Create(HINSTANCE hInstance, LPCTSTR className, Window::WndProc ptr, DWORD exStyle, DWORD style, UINT classStyle, HBRUSH hbrBackground, HCURSOR hCursor, HICON hIcon, HWND hWndParent, HMENU hMenu, LPVOID lpParam)
{
	wndProcPtr = ptr;

	Window::Create(hInstance, className, WndProc, exStyle, style, classStyle, hbrBackground, hCursor, hIcon, hWndParent, hMenu, lpParam);

	BOOL dwmEnabled;
	DwmIsCompositionEnabled(&dwmEnabled);

	if (dwmEnabled)
	{
		DwmExtendFrameIntoClientArea(this->GetHWnd(),&glassMargins);
	}
}

void CustomWindow::SetFrameMargins(int cxLeftWidth, int cxRightWidth, int cyTopHeight, int cyBottomHeight)
{
	MARGINS m = {cxLeftWidth, cxRightWidth, cyTopHeight, cyBottomHeight};
	SetFrameMargins(m);
}

void CustomWindow::SetFrameMargins(int all)
{
	MARGINS m = {all, all, all, all};
	SetFrameMargins(m);
}

void CustomWindow::SetFrameMargins(MARGINS m)
{
	frameMargins = m;
}

MARGINS CustomWindow::GetFrameMargins()
{
	return frameMargins;
}

void CustomWindow::SetGlassMargins(int cxLeftWidth, int cxRightWidth, int cyTopHeight, int cyBottomHeight)
{
	MARGINS m = {cxLeftWidth, cxRightWidth, cyTopHeight, cyBottomHeight};
	SetGlassMargins(m);
}

void CustomWindow::SetGlassMargins(int all)
{
	MARGINS m = {all, all, all, all};
	SetGlassMargins(m);
}

void CustomWindow::SetGlassMargins(MARGINS m)
{
	glassMargins = m;

	BOOL dwmEnabled;
	DwmIsCompositionEnabled(&dwmEnabled);

	if (dwmEnabled)
		DwmExtendFrameIntoClientArea(this->GetHWnd(),&glassMargins);
}

MARGINS CustomWindow::GetGlassMargins()
{
	return glassMargins;
}

LRESULT CustomWindow::HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam, MARGINS margins)
{
    // Get the point coordinates for the hit test.
    POINT ptMouse = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};

    // Get the window rectangle.
    RECT rcWindow;
    GetWindowRect(hWnd, &rcWindow);

    // Get the frame rectangle, adjusted for the style without a caption.
    RECT rcFrame = { 0 };
    AdjustWindowRectEx(&rcFrame, WS_OVERLAPPEDWINDOW & ~WS_CAPTION, FALSE, NULL);

    // Determine if the hit test is for resizing. Default middle (1,1).
    USHORT uRow = 1;
    USHORT uCol = 1;
    bool fOnResizeBorder = false;

    // Determine if the point is at the top or bottom of the window.
	if (ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + margins.cyTopHeight)
    {
        fOnResizeBorder = (ptMouse.y < (rcWindow.top - rcFrame.top));
        uRow = 0;
    }
	else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - margins.cyBottomHeight)
    {
        uRow = 2;
    }

    // Determine if the point is at the left or right of the window.
	if (ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + margins.cxLeftWidth)
    {
        uCol = 0; // left side
    }
	else if (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - margins.cxRightWidth)
    {
        uCol = 2; // right side
    }

    // Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
    LRESULT hitTests[3][3] = 
    {
        { HTTOPLEFT,    fOnResizeBorder ? HTTOP : HTCAPTION,    HTTOPRIGHT },
        { HTLEFT,       HTNOWHERE,     HTRIGHT },
        { HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT },
    };

    return hitTests[uRow][uCol];
}

LRESULT CustomWindow::CustomCaptionProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool* pCancel)
{
    LRESULT lRet = 0;
    bool cancel = false;

    cancel = DwmDefWindowProc(hWnd, message, wParam, lParam, &lRet);

    // Handle window creation.
    if (message == WM_CREATE)
    {
        RECT rcClient;
        GetWindowRect(hWnd, &rcClient);

        // Inform application of the frame change.
        SetWindowPos(hWnd, 0, rcClient.left, rcClient.top, RECTWIDTH(rcClient), RECTHEIGHT(rcClient), SWP_FRAMECHANGED);
    }

    // Handle window activation.
    if (message == WM_ACTIVATE)
    {
		DwmExtendFrameIntoClientArea(hWnd,&((CustomWindow*)GetInstance(hWnd))->glassMargins);
    }
	   
    // Handle the non-client size message.
    if (message == WM_NCCALCSIZE && wParam == TRUE)
    {
        cancel = true;
    }

    // Handle hit testing in the NCA if not handled by DwmDefWindowProc.
    if ((message == WM_NCHITTEST) && (lRet == 0))
    {
        lRet = HitTestNCA(hWnd, wParam, lParam, ((CustomWindow*)GetInstance(hWnd))->frameMargins);

		if (lRet != HTNOWHERE)
            cancel = true;
    }

    *pCancel = cancel;
    return lRet;
}

LRESULT CALLBACK CustomWindow::WndProc(LPVOID sender, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    bool cancel = false;
	LRESULT lRet = 0;
    static BOOL dwmEnabled = -1;

	if (dwmEnabled == -1)
		DwmIsCompositionEnabled(&dwmEnabled);

	if (message == WM_DWMCOMPOSITIONCHANGED)
	{
		DwmIsCompositionEnabled(&dwmEnabled);

		if (dwmEnabled)
		{
			DwmExtendFrameIntoClientArea(hWnd,&((CustomWindow*)GetInstance(hWnd))->glassMargins);
		}

		RECT rcClient;
		GetWindowRect(hWnd, &rcClient);

		// Inform application of the frame change.
		SetWindowPos(hWnd, 0, rcClient.left, rcClient.top, RECTWIDTH(rcClient), RECTHEIGHT(rcClient), SWP_FRAMECHANGED);	
	}

	if (dwmEnabled)
		lRet = CustomCaptionProc(hWnd, message, wParam, lParam, &cancel);

	if (!cancel)
	{
		if (((CustomWindow*)GetInstance(hWnd))->wndProcPtr)
			lRet = ((CustomWindow*)GetInstance(hWnd))->wndProcPtr(sender, hWnd, message, wParam, lParam);
		else
			lRet = DefWindowProc(hWnd, message, wParam, lParam);
	}

	return lRet;
}

